#include "transliterator.h"
#include "transmap.h"
#include <QStringList>
#include <stdio.h>



#define ELEMENT_MAX_LEN 3
#define VIRAMA 0x94D
#define VOWEL_A 0x905

//akaWora
class QString;

QString filter_itrans(QString itrans_str)
{
  QString tmp = itrans_str;
  tmp = tmp.replace("x", "kSh").replace("GY","j~n").replace("dny","j~n");
  return tmp;
}

static int calc_depvowel(int c)
{
  //according to http://www.unicode.org/charts/PDF/U0900.pdf
  //for special treatment.
  //it do not always follow the offset.
  if(c == 0x90C)
      return 0x962;
  if(c == 0x960)
      return 0x944;
  if(c == 0x961)
      return 0x963;
  return c + 0x93E - 0x906;
}

static int calc_fullvowel(int c)
{
  if(c == 0x962)
    return 0x90C;
  if(c == 0x944)
    return 0x960;
  if(c == 0x963)
    return 0x961;
  if(c>=0x93E && c<=0x94C)
    return c - (0x93E - 0x906);
  return c;
}


static int is_consonant(int c)
{
 if(c>=0x915 && c<=0x939)
   return 1;
 if(c>=0x958 && c<=0x95F)
   return 1;
 if(c>=0x979 && c<=0x97C)
   return 1;
 if(c>=0x97E && c<=0x97F)
   return 1;
 if(c==0x001)
   return 1;
 return 0;
}

static int is_depvowel(int c)
{
    if( c == 0x962 || c == 0x944 || c == 0x963 ||(c>=0x93E && c<=0x94C))
        return 1;
    return 0;
}


static int is_vowel(int c)
{
  if(c>=0x93E && c<=0x94C)
    return 1;
  if(c>=0x906 && c<=0x914)
    return 1;
  if(c == 0x960 || c == 0x961 || c == 0x962 || c == 0x963)
    return 1;
  return 0;
}

static int find_uchar_index(int u8ch)
{
  int length = sizeof(uni_map)/sizeof(const int);
  int i;
  for(i = 0; i < length; i++) {
    if(uni_map[i] == u8ch)
      return i;
  }
  return -1;
}


Transliterator::Transliterator()
{
    int length = sizeof(uni_map)/sizeof(int);
    for(int i = 0; i < length; i++){
        itrans << itrans_map[i];
        hk<<hk_map[i];
        slp1<<slp1_map[i];
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        iast<<iast_map[i];
        iso15919<<iso15919_map[i];
#else
        iast<<QString::fromLatin1(iast_map[i]);
        iso15919<<QString::fromLatin1(iso15919_map[i]);
#endif
        velthuis<<velthuis_map[i];
        unicode.push_back(uni_map[i]);
    }
}


bool Transliterator::is_devanagari(QString text)
{
    text = text.simplified().replace(" ", "");
    for(int i = 0; i < text.length(); i++) {
        QChar c = text.at(i);
        if(c<0x900 || c>0x97F) {
            return false;
        }
    }
    return true;
}

QString Transliterator::convert(QString src, QStringList src_map, QStringList dest_map)
{
    if(src.isNull() || src.isEmpty())
        return "";
    //printf("src: %s\n", src.toStdString().c_str());
    int len = src.length();
    QString dest_str;

    //where we are currently in src string.
    int cursor = 0;

    //the max length of all table's element is 3;
    //first we try read the max length;
    int read_len = ELEMENT_MAX_LEN;

    while(read_len) {
        //int should_read_len = (len-cursor) > ELEMENT_MAX_LEN?ELEMENT_MAX_LEN:(len-cursor);
        QString readin = src.mid(cursor, read_len);
        int ret = src_map.indexOf(readin);
        if(ret != -1) {
            cursor += read_len;
            dest_str = dest_str + dest_map.at(ret);
            read_len = (len-cursor) > ELEMENT_MAX_LEN?ELEMENT_MAX_LEN:(len-cursor);
            if(read_len < 0)
              read_len = 0;
        } else {
        //only one char left and still can not find a matched one in orig_table
        //that means it is can not be supported or no need to trans
        //here just append it to the end of dest string.
        if(read_len == 1) {
            cursor += 1;
            dest_str = dest_str + readin;
            read_len = (len-cursor) > ELEMENT_MAX_LEN?ELEMENT_MAX_LEN:(len-cursor);
        } else if(read_len > 0)
          read_len -= 1;
      }
    }

    return dest_str;
}


QString Transliterator::filter_itrans(QString itrans_str)
{
  QString tmp = itrans_str;
  tmp = tmp.replace("x", "kSh").replace("GY","j~n").replace("dny","j~n");
  return tmp;
}

QString Transliterator::itrans_to_iast(QString src)
{
  QString str = filter_itrans(src);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  return convert(str, itrans, iast);
#else
  return QString::fromUtf8(convert(str, itrans, iast).toLatin1().constData());
#endif
}

QString Transliterator::iast_to_itrans(QString src)
{
  return convert(src, iast, itrans);
}

QString Transliterator::itrans_to_iso15919(QString src)
{
    QString str = filter_itrans(src);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    return convert(str, itrans, iso15919);
#else
    return QString::fromUtf8(convert(str, itrans, iso15919).toLatin1().constData());
#endif
}

QString Transliterator::iso15919_to_itrans(QString src)
{

    return convert(src, iso15919, itrans);
}

QString Transliterator::slp1_to_itrans(QString src)
{
    return convert(src, slp1, itrans);
}

QString Transliterator::itrans_to_slp1(QString src)
{
    QString str = filter_itrans(src);
    return convert(str, itrans, slp1);
}

QString Transliterator::hk_to_itrans(QString src)
{
    return convert(src, hk, itrans);
}

QString Transliterator::itrans_to_hk(QString src)
{
    QString str = filter_itrans(src);
    return convert(str, itrans, hk);
}

QString Transliterator::velthuis_to_itrans(QString src)
{
    return convert(src, velthuis, itrans);
}

QString Transliterator::itrans_to_velthuis(QString src)
{
    QString str = filter_itrans(src);
    return convert(str, itrans, velthuis);
}

QString Transliterator::itrans_to_deva(QString src)
{
    if(src.isNull() || src.isEmpty())
        return "";
  QString replace = src.replace("x", "kSh").replace("GY","j~n").replace("dny","j~n");
  int len = replace.length();

  QString dest_str;

  //where we are currently in src string.
  int cursor = 0;

  //the max length of all table's element is 3;
  //first we try read the max length;
  int read_len = ELEMENT_MAX_LEN;

  int previous_is_consonant = 0;
  while(read_len) {
    QString tmp = src.mid(cursor, read_len);
    int ret = itrans.indexOf(tmp);
    //if found, then set cursor to new position.
    //copy the matched one in dest_table to dest_str.
    //re-calculate the read_len.
    if(ret != -1) {
      cursor += read_len;

      int uni_num = unicode.at(ret);

      if(previous_is_consonant && is_vowel(uni_num) ) {
        uni_num = calc_depvowel(uni_num);
        dest_str.chop(1);
        dest_str.append(QChar(uni_num));
        previous_is_consonant = 0;
      } else if(is_consonant(uni_num)){
          if(uni_num != 0x001) {
            dest_str.append(QChar(uni_num));
            dest_str.append(QChar(VIRAMA));
          } else {
              dest_str.append(QChar(0x933));
              dest_str.append(QChar(VIRAMA));
              dest_str.append(QChar(0x939));
              dest_str.append(QChar(VIRAMA));
          }
          previous_is_consonant = 1;
      } else if(uni_num == VOWEL_A) {
        if(!previous_is_consonant) {
            dest_str.append(QChar(uni_num));
        } else {
            dest_str.chop(1);
        }
        previous_is_consonant = 0;
      } else {
            dest_str.append(QChar(uni_num));
            previous_is_consonant = 0;
      }

      read_len = (len-cursor) > ELEMENT_MAX_LEN?ELEMENT_MAX_LEN:(len-cursor);
      if(read_len <= 0)
        read_len = 0;
    }
    else {
      //only one char left and still can not find a matched one in orig_table
      //that means it is can not be supported or no need to trans
      //here just append it to the end of dest string.
      if(read_len == 1) {
        cursor += 1;
        dest_str.append(tmp);
        read_len = (len-cursor) > ELEMENT_MAX_LEN?ELEMENT_MAX_LEN:(len-cursor);
        previous_is_consonant = 0;
      } else if(read_len > 0)
        read_len -= 1;
    }
  }

  return dest_str;
}


QString Transliterator::deva_to_itrans(QString deva)
{
    if(deva.isNull() || deva.isEmpty())
        return "";
    QString src = deva;//QString::fromUtf8(deva.toLatin1());

    QString itrans_str = "";

    int length = src.length();
    int previous_is_consonant = 0;
    int need_append_vowel_a = 0;

    for(int i = 0; i < length; i++){
        QChar c = src.at(i);
        ushort uch = c.unicode();
        if(previous_is_consonant && uch == VIRAMA) {
            previous_is_consonant = 0;
            continue;
        }
        if(previous_is_consonant && !is_depvowel(uch)) {
            itrans_str = itrans_str + "a";
            previous_is_consonant = 0;
        }
        //the last char is a consonant.
        //NOTE, if a sanskrit word really end with consonant, the last char is halant.
        if(is_consonant(uch) && i == length-1){
            need_append_vowel_a = 1;
        }
        if(is_depvowel(uch)) {
          uch = calc_fullvowel(uch);
          previous_is_consonant = 0;
        }
        if(is_consonant(uch))
          previous_is_consonant = 1;

        int found = find_uchar_index(uch);
        if(found != -1) {

          itrans_str = itrans_str + QString(itrans_map[found]);
        } else {
          itrans_str.append(c);
          previous_is_consonant = 0;
        }
    }
    if(need_append_vowel_a)
        itrans_str += "a";

    return itrans_str;
}
